<template>
  <div>
    <el-alert
        v-if="env === 'production'"
        :title="'当前环境 ' + env + ' 该功能仅在开发模式使用'"
        type="error"
    />
    <h1>
      一键搭建功能
      <el-switch
          size="small"
          v-model="IDEASettingTutorial"
          inline-prompt
          style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
          active-text="关闭教程"
          inactive-text="IDEA配置"
      />
    </h1>

    <div class="main">
      <div class="form">
        <el-form
            ref="ruleFormRef"
            :model="form"
            :rules="rules"
            size="small"
            label-width="auto"
            class="form-inline"
        >
          <el-form-item label="名称" prop="name">
            <el-input v-model="form.name" placeholder="名称" clearable />
          </el-form-item>
          <el-form-item label="数据源" prop="source">
            <el-select
                v-if="optionDatas[dataSourceOptionId]"
                filterable
                v-model="form.source"
                placeholder="Select"
                style="width: 250px"
            >
              <el-option
                  v-for="(item, index) in optionDatas[dataSourceOptionId].options"
                  :key="index"
                  :label="item.label"
                  :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="菜单位置" prop="menuId">
            <el-select
                v-if="optionDatas[menuTreeOptionId]"
                filterable
                v-model="form.menuId"
                placeholder="Select"
                style="width: 250px"
            >
              <el-option
                  v-for="(item, index) in optionDatas[menuTreeOptionId].options"
                  :key="index"
                  :label="item.label"
                  :value="item.value"
              />
            </el-select>
          </el-form-item>
          <el-form-item label="图标" prop="icon">
            <el-input v-model="form.icon" placeholder="图标" clearable />
          </el-form-item>
          <el-form-item label="添加到当前用户角色" prop="role">
            <el-select
                v-if="optionDatas[roleOptionId]"
                filterable
                v-model="form.roles"
                placeholder="Select"
                style="width: 250px"
                multiple
            >
              <el-option
                  v-for="(item, index) in optionDatas[roleOptionId].options"
                  :key="index"
                  :label="item.label"
                  :value="item.value"
                  :disabled="!roles.includes(item.value)"
              />
            </el-select>
          </el-form-item>
        </el-form>
        <div v-if="env !== 'production'">
          <el-button type="success" @click="start" :disabled="loading">{{
              loading ? '生成中...' : '开始'
            }}</el-button>
          <!-- <el-button type="success" @click="restart(4)">手动重启</el-button> -->
          <el-button type="success" @click="failHandle(myFlow)" :disabled="loading">回退</el-button>
          <el-button-group style="margin: 0 10px"
          ><el-button type="warning" @click="() => myFlow.pause()" :disabled="!loading"
          >暂停</el-button
          ><el-button type="success" @click="() => myFlow.resume()" :disabled="!loading"
          >恢复</el-button
          ></el-button-group
          ><el-button type="danger" @click="() => myFlow.forceStop()" :disabled="!loading"
        >强制结束</el-button
        >
        </div>
      </div>
      <div class="setting" v-if="IDEASettingTutorial">
        <el-scrollbar height="72vh">
          <el-text class="mx-1" size="large" tag="b">▶IDEA 配置教程</el-text><br />
          <el-text class="mx-1" size="large" tag="b">1️⃣第一步</el-text>
          <el-image class="setting-img" src="/restart1.jpg" fit="contain"></el-image>
          <el-image class="setting-img" src="/restart2.jpg" fit="contain"></el-image><br />
          <el-text class="mx-1" size="large" tag="b">2️⃣第二步(IDEA 2023.3+ 适用)</el-text>
          <el-image class="setting-img" src="/restart3.jpg" fit="contain"></el-image>
          <el-image class="setting-img" src="/restart4.jpg" fit="contain"></el-image><br />
          <el-text class="mx-1" size="large" tag="b">2️⃣第二步(IDEA 2023.3- 适用)</el-text>
          <el-image class="setting-img" src="/restart5.jpg" fit="contain"></el-image
          ></el-scrollbar>
      </div>
      <div class="console">
        <el-text class="mx-1" size="large">日志</el-text>
        <div class="console-log">
          <el-scrollbar ref="scrollbarRef" height="67vh">
            <div v-for="(log, index) in consoleLog" :key="index">
              <el-text class="mx-1" :type="log.type ? log.type : 'info'"
              >[ {{ moment(log.time).format('YYYY-MM-DD HH:mm:ss') }} ] {{ log.value }}</el-text
              >
            </div>
            <span v-loading="loading"></span>
          </el-scrollbar>
        </div>
        <el-button type="info" size="small" @click="clear">清空</el-button>
        <el-button size="small" @click="top">回到顶部</el-button>
        <el-button size="small" @click="bottom">到底部</el-button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onBeforeMount, computed, nextTick } from 'vue'
import { useOptionDataStore } from '@/stores/optionData'
import { storeToRefs } from 'pinia'
import FaSysDataSource from '@/api/sys/FaSysDataSource'
import FaSysGen from '@/api/sys/FaSysGen'
import FaSysMenu from '@/api/sys/FaSysMenu'
import FaSysRoleMenu from '@/api/sys/FaSysRoleMenu'
import Other from '@/api/other'
// 导入api接口
import api from '@/api'
import FaSysGrid from '@/api/sys/FaSysGrid'
import { toProperCase } from '@/utils/str'
import TaskFlow from '@/utils/TaskFlow'
const member = JSON.parse(sessionStorage.getItem('member'))
const roles = JSON.parse(member.roleIds)
// 获取权限模版列表
const gridFunctuin = sessionStorage.getItem('functionRole')
import moment from 'moment'
const env = computed(() => import.meta.env.VITE_MODE_NAME)
const storeOptionData = useOptionDataStore()
const { optionDatas } = storeToRefs(storeOptionData)
const dataSourceOptionId = 'a00baa42697094079b2f1a9041823d33'
const menuTreeOptionId = '861a309c9bfdaf1436b306ab0f46087b'
const roleOptionId = '697a1e829982bae3b51e28fb9b72a861'
const dataSourceGridId = '1b780e7ee96827b13a882c801db06c9d'
const ruleFormRef = ref()
const IDEASettingTutorial = ref(false)
const scrollbarRef = ref()
const form = ref({
  type: 'table'
})
const rules = ref({
  name: [
    {
      required: true,
      message: '请输入名称',
      trigger: 'blur'
    }
  ],
  source: [
    {
      required: true,
      message: '请选择数据源',
      trigger: 'blur'
    }
  ]
})
const loading = ref(false)
const consoleLog = ref([])
onBeforeMount(() => {
  storeOptionData.add(dataSourceOptionId)
  storeOptionData.add(menuTreeOptionId)
  storeOptionData.add(roleOptionId)
})

function clear() {
  consoleLog.value.length = 0
}
// 滚动到顶部
function top() {
  scrollbarRef.value.scrollTo({ top: 0, behavior: 'smooth' })
}
// 滚动到底部
function bottom() {
  nextTick(() => {
    scrollbarRef.value.scrollTo({
      top: scrollbarRef.value.wrapRef.scrollHeight,
      behavior: 'smooth'
    })
  })
}

/**
 * 添加日志
 * @param {*} log
 */
function addLog(log) {
  if (log.ifRepeat && consoleLog.value[consoleLog.value.length - 1].ifRepeat) {
    consoleLog.value.pop()
  }
  consoleLog.value.push({ ...log, time: new Date() })
  // 滚动到底部
  bottom()
}

const startTime = ref(null)
// 2. 定义任务列表（混合同步/异步任务）
const taskList = [
  {
    name: '检查输入参数',
    type: 'async',
    handler: paramCheck
  },
  {
    name: '检查表格',
    type: 'async',
    handler: tableCheck,
    startLog: false
  },
  {
    name: '创建数据源',
    type: 'async',
    handler: addDataSource,
    startLog: false
  },
  {
    name: '查询数据源id',
    type: 'async',
    handler: getDataSourceId
  },
  {
    name: '生成代码',
    type: 'async',
    handler: genCode
  },
  {
    name: '检查代码是否生成',
    type: 'async',
    handler: checkCode
  },
  {
    name: '重启',
    type: 'async',
    handler: restartHandle
  },
  {
    name: '检查服务端重启状态',
    type: 'async',
    handler: doSomething
  },
  {
    name: '创建网格',
    type: 'async',
    handler: createGrid
  },
  {
    name: '获取网格id',
    type: 'async',
    handler: getGridId
  },
  {
    name: '创建菜单',
    type: 'async',
    handler: createMenu
  },
  {
    name: '获取菜单id',
    type: 'async',
    handler: getMenuId
  },
  {
    name: '添加角色权限',
    type: 'async',
    handler: addPower
  }
]
// 3. 创建流程实例并配置生命周期钩子
const myFlow = new TaskFlow({
  tasks: taskList,
  logs: consoleLog.value,
  onStart: function () {
    addLog({
      value: '=== 流程开始 ===',
      type: 'success'
    })
    // 开始加载动画
    loading.value = true
    // 开始计时
    startTime.value = new Date()
  },
  onBefore: function (currentTask) {
    if (currentTask.startLog) {
      addLog(currentTask.startLog)
    } else if (currentTask.startLog === undefined) {
      addLog({
        value: `[${this.state.currentTaskIndex + 1}]. ${currentTask.name}`,
        type: 'success'
      })
    }
  },
  onPause: function () {
    addLog({
      value: '=== 流程暂停 ===',
      type: 'warning'
    })
  },
  onResume: function () {
    addLog({
      value: '=== 流程恢复 ===',
      type: 'primary'
    })
  },
  onForceStop: function () {
    addLog({
      value: `=== 流程被强制结束 ===`,
      type: 'danger'
    })
  },
  onTaskFail: function (err, currentTask) {
    // 失败回滚
    failHandle(this)
    addLog({
      value: `=== 任务${currentTask.name}失败：${err.message} ===`,
      type: 'danger'
    })
  },
  onComplete: function (allResults) {
    addLog({
      value: `=== 流程全部完成 ===`,
      type: 'success'
    })
    addLog({
      value: `所有任务结果：${allResults}`,
      type: 'success'
    })
  },
  onEnd: function () {
    // 判断是否触发强制停止，需要在流程全部结束后再执行回滚
    if (this.state.isForceStopped) {
      // 失败回滚
      failHandle(this)
    }
    addLog({
      value: `完成
        time: ${Date.now() - startTime.value} ms`,
      type: 'success'
    })
    // 结束加载动画
    loading.value = false
    // 初始化参数
    this.initialization()
  }
})

function start() {
  myFlow.start()
}
async function paramCheck(historyResults, index) {
  // 参数检查
  await new Promise((resolve) => {
    ruleFormRef.value
        .validate((valid, fields) => {
          if (valid) {
            addLog({
              value: '参数校验 ok',
              type: 'success'
            })
          } else {
            addLog({
              value: '参数校验失败!',
              type: 'danger'
            })
            // 强制结束流程
            this.forceStop()
          }
        })
        .finally(() => {
          resolve()
        })
  })
}

async function tableCheck(historyResults, index) {
  const tableName = form.value.source
  addLog({
    value: `开始检查表格 [${tableName}]`
  })
  return await new Promise((resolve) => {
    new Other()
        .tableCheck(tableName)
        .then((res) => {
          if (res.code === 200) {
            addLog({
              value: `检查表格 [${tableName}] ok`,
              type: 'success'
            })
          } else {
            addLog({
              value: `检查表格 [${tableName}] 失败 [${res.msg}]`,
              type: 'danger'
            })
            // 强制结束流程
            this.forceStop()
          }
        })
        .catch((err) => {
          console.log(err)
          addLog({
            value: `检查表格失败 [${err.msg}]`,
            type: 'danger'
          })
          // 强制结束流程
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}

const dataSource = ref(null)
async function addDataSource(historyResults, index) {
  addLog({
    value: `创建数据源 [${form.value.name}]`
  })
  return await new Promise((resolve) => {
    new FaSysDataSource(dataSourceGridId)
        .add({ name: form.value.name, source: form.value.source, type: 'table' })
        .then((res) => {
          addLog({
            value: '创建数据源 ok',
            type: 'success'
          })
        })
        .catch((err) => {
          addLog({
            value: `创建数据源失败 [${err.msg}]`,
            type: 'danger'
          })
          // 强制结束流程
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}

async function getDataSourceId(historyResults, index) {
  return await new Promise((resolve) => {
    new FaSysDataSource(dataSourceGridId)
        .getByParam({ name: form.value.name })
        .then((res) => {
          // 获取数据源Id
          dataSource.value = res.data
          addLog({
            value: `查询数据源id [${dataSource.value.id}]`
          })
          resolve(dataSource.value.id)
        })
        .catch((err) => {
          addLog({
            value: `查询数据源id失败 [${err.msg}]`,
            type: 'danger'
          })
          // 强制结束流程
          this.forceStop()
          resolve()
        })
  })
}

// 生成代码
async function genCode(historyResults, index) {
  const dataSourceId = historyResults[historyResults.length - 1]
  return await new Promise((resolve) => {
    new FaSysGen()
        .genCodeByDataSourceId(dataSourceId)
        .then(async (res) => {
          addLog({
            value: '生成代码 ok',
            type: 'success'
          })
        })
        .catch((err) => {
          addLog({
            value: `生成代码失败 [${err.msg}]`,
            type: 'danger'
          })
          // 强制结束流程
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}

// 检查代码是否生成
async function checkCode(historyResults, index) {
  let result = true
  while (result) {
    delay(1000)
    result = await new Promise((resolve) => {
      new FaSysGen()
          .checkCode(dataSource.value.genId)
          .then((res) => {
            // res.data = true 表示代码检查通过
            addLog({
              value: `检查代码生成结果 ${res.data}`,
              type: 'danger'
            })
            resolve(res.data ? false : true)
          })
          .catch((err) => {
            addLog({
              value: `检查代码失败 [${err.msg}]`,
              type: 'danger'
            })
            // 强制结束流程
            this.forceStop()
            resolve(false)
          })
    })
  }
}

async function restartHandle(historyResults, index) {
  // 重启
  const restartNum = 0
  await new Promise((resolve) => restart(restartNum, resolve, this))
}

function restart(restartNum, resolve, $this) {
  // 最多允许重启五次
  if (restartNum === 5) {
    // 强制结束流程
    $this.forceStop()
    resolve()
    return
  }
  addLog({
    value: '开始重启服务端代码...'
  })
  restartNum++
  new Other()
      .restart()
      .then(async () => {
        addLog({
          value: '重启服务端请求成功！等待检查是否重启成功！'
        })
        await delay(20000)
        resolve()
      })
      .catch((err) => {
        addLog({
          value: `重启服务端代码失败 [${err.msg}]`,
          type: 'danger'
        })
        restart(restartNum, resolve, $this)
      })
}

async function delay(ms) {
  for (let i = 0; i <= ms; i += 1000) {
    await new Promise((resolve) => {
      setTimeout(resolve, 1000)
      addLog({
        ifRepeat: true,
        value: '等待 ' + i / 1000 + '/' + ms / 1000 + ' 秒...'
      })
    })
  }
}

async function doSomething(historyResults, index) {
  let whileState = true
  let checkCount = 0
  while (whileState) {
    addLog({
      value: '服务端重启检查中...' + checkCount
    })
    // 检查是否重启成功
    whileState = await checkRestart()
    if (!whileState) {
      addLog({
        value: '重启服务端代码成功',
        type: 'success'
      })
      break
    }
    // 判断检查次数是否超过10次
    if (checkCount && checkCount % 2 === 0) {
      addLog({
        value: '重启服务端代码超时-再次重启 ' + (checkCount % 10),
        type: 'danger'
      })
      await restartHandle()
    }
    await delay(10000)
    // 检查次数+1
    checkCount++
  }
}
// 检查重启结果
async function checkRestart() {
  // 拼接接口
  const apiRouter = toProperCase(form.value.source, '_')
  return await new Promise((resolve) => {
    api
        .getApi(apiRouter, null)
        .list()
        .then((res) => {
          addLog({
            value: '请求成功 ' + res.code,
            type: 'success'
          })
          resolve(false)
        })
        .catch((err) => {
          addLog({
            value: '请求失败 ' + err,
            type: 'danger'
          })
          resolve(true)
        })
  })
}
async function createGrid(historyResults, index) {
  // 拼接接口
  const apiRouter = toProperCase(form.value.source, '_')
  return await new Promise((resolve) => {
    new FaSysGrid('85f653190582ddef01eeed6334ceed06')
        .add({
          name: form.value.name,
          dataSourceId: dataSource.value.id,
          editDataSourceId: dataSource.value.id,
          api: apiRouter,
          editApi: apiRouter,
          note: '快速生成'
        })
        .then((res) => {
          addLog({
            value: '创建网格 ok',
            type: 'success'
          })
        })
        .catch((err) => {
          addLog({
            value: `创建网格失败 [${err.msg}]`,
            type: 'danger'
          })
          // 强制结束流程
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}
//网格id
const gridId = ref(null)
async function getGridId(historyResults, index) {
  return await new Promise((resolve) => {
    new FaSysGrid('85f653190582ddef01eeed6334ceed06')
        .getByParam({ name: form.value.name })
        .then((res) => {
          // 获取网格Id
          gridId.value = res.data.id
          addLog({
            value: `查询网格id [${gridId.value}]`
          })
        })
        .catch((err) => {
          addLog({
            value: `查询网格id失败 [${err.msg}]`,
            type: 'danger'
          })
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}
async function createMenu(historyResults, index) {
  // 拼接接口
  const apiRouter = toProperCase(form.value.source, '_')
  await new Promise((resolve) => {
    new FaSysMenu('33cca5225ebd7ed988cf840a2642e6fb')
        .add({
          name: form.value.name,
          routerPath: '/home/' + toProperCase(form.value.source, '_'),
          grids: gridId.value,
          router: JSON.stringify({
            path: apiRouter,
            name: form.value.name,
            meta: {
              componentPath: '/src/views/template/base/IndexView.vue',
              keepAlive: true
            }
          }),
          otherRouterJson: '[]',
          type: 'user',
          pid: form.value.menuId,
          icon: form.value.icon
        })
        .then((res) => {
          addLog({
            value: '创建菜单 ok',
            type: 'success'
          })
        })
        .catch((err) => {
          addLog({
            value: `创建菜单失败 [${err.msg}]`,
            type: 'danger'
          })
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}
const menuId = ref(null)
async function getMenuId(historyResults, index) {
  await new Promise((resolve) => {
    new FaSysMenu('33cca5225ebd7ed988cf840a2642e6fb')
        .getByParam({ name: form.value.name })
        .then((res) => {
          // 获取菜单Id
          menuId.value = res.data.id
          addLog({
            value: `查询菜单id [${menuId.value}]`
          })
        })
        .catch((err) => {
          addLog({
            value: `查询菜单id失败 [${err.msg}]`,
            type: 'danger'
          })
          this.forceStop()
        })
        .finally(() => {
          resolve()
        })
  })
}
async function addPower(historyResults, index) {
  // 添加角色权限
  for (const index in form.value.roles) {
    const roleId = form.value.roles[index]
    await new Promise((resolve) => {
      new FaSysRoleMenu('33861f55d9473b6d18b07bc00d5857ed')
          .add({
            roleId,
            gridId: gridId.value,
            menuId: menuId.value,
            functionRole: gridFunctuin
          })
          .then((res) => {
            addLog({
              value: `添加角色权限成功 [${roleId}]`
            })
          })
          .catch((err) => {
            addLog({
              value: `添加角色权限失败 ${err.msg}`,
              type: 'danger'
            })
            this.forceStop()
          })
          .finally(() => {
            resolve()
          })
    })
  }
}
// 失败回滚
function failHandle() {
  addLog({
    value: '回滚...',
    type: 'danger'
  })
  // 删除数据源
  if (dataSource.value) {
    new FaSysDataSource(dataSourceGridId).del(dataSource.value.id).then(() => {
      addLog({
        value: '删除数据源成功',
        type: 'success'
      })
    })
    // 删除代码文件
    new FaSysGen().delCode(dataSource.value.genId).then(() => {
      addLog({
        value: '删除代码文件成功',
        type: 'success'
      })
    })
  }

  // 删除角色权限
  if (menuId.value) {
    for (const key in form.value.roles) {
      const roleId = form.value.roles[key]
      // 查询角色权限id
      new FaSysRoleMenu('33861f55d9473b6d18b07bc00d5857ed')
          .listNotPage({ roleId, menuId: menuId.value })
          .then((res) => {
            const roleMenuId = res.data[0].id
            new FaSysRoleMenu('33861f55d9473b6d18b07bc00d5857ed').del(roleMenuId).then(() => {
              addLog({
                value: `删除角色权限成功 [${roleId}]`
              })
            })
          })
    }

    // 删除菜单
    new FaSysMenu('33cca5225ebd7ed988cf840a2642e6fb').del(menuId.value).then(() => {
      addLog({
        value: '删除菜单成功',
        type: 'success'
      })
    })
  }
  // 删除网格
  if (gridId.value) {
    new FaSysGrid('85f653190582ddef01eeed6334ceed06').del(gridId.value).then(() => {
      addLog({
        value: '删除网格成功',
        type: 'success'
      })
    })
  }
}
</script>

<style scoped>
.main {
  display: flex;
  height: 75vh;
}
.form {
  /* flex-grow: 1; */
  margin-right: 10px;
}

.form-inline .el-input {
  --el-input-width: 250px;
}
.setting {
  /* flex-grow: 1; */
  width: 45vw;
  margin: 0 10px;
  padding: 10px;
  border: 2px dashed var(--el-border-color);
}
.setting .mx-1 {
  line-height: 30px;
}
.setting-img {
  width: 95%;
  margin-bottom: 10px;
  margin-left: 20px;
}
.console {
  flex-grow: 1;
  /* width: 25vw; */
  margin-left: 10px;
  background-color: #fdf9f9;
  padding: 10px;
  box-shadow: var(--el-box-shadow-lighter);
}
.console-log {
  border-radius: 5px;
  background-color: #2b2c2e;
  opacity: 0.9;
  padding-left: 10px;
  margin-bottom: 10px;
  box-shadow: inset var(--el-box-shadow-lighter);
}

:deep(.circular) {
  height: 20px;
  width: 20px;
  margin-top: 10px;
}
</style>
